require'socket'
local copas = require 'copas1' 
format = string.format

--copas.FAST_ACCEPT = true

local function header(l)
    local k,v = l:match("^%s*([^:]*):(.*)$")
    return k:match("^%s*(.-)%s*$"), v:match("^%s*(.-)%s*$")
end

local function keep_alive(proto, c)
    if proto:find('1.0') and c == 'keep-alive' then
        return "Connection: Keep-Alive\r\n"
    end
end

local function H501(obj,verb, h,uri,proto)
    local data
    local alive = keep_alive(proto, h['connection'])
    obj:send(format("%s 501 No Implemented\r\n", proto))
    if alive then obj:send(alive) end
    obj:send("Content-Type: plain/text\r\n")
    local r  = format("%s %s\r\n", verb, uri)
    obj:send(format("Content-Length: %i\r\n", #r))
    obj:send("\r\n")
    obj:send(r)
end

local function H404(obj,verb, h,uri,proto)
    local data
    local alive = keep_alive(proto, h['connection'])
    obj:send(format("%s 404 Not Found\r\n", proto))
    if alive then obj:send(alive) end
    obj:send("Content-Type: plain/text\r\n")
    local r  = format("%s %s\r\n", verb, uri)
    obj:send(format("Content-Length: %i\r\n", #r))
    obj:send("\r\n")
    obj:send(r)
end

local dispatch={
    ['GET'] = function(obj,verb, h,uri,proto)
        local data= format("%s %s %s\r\n", verb, uri, "hello world")
        local alive = keep_alive(proto, h['connection'])
        obj:send(format("%s 200 OK\r\n",proto))
        --local too_old = false and os.difftime(os.time(), obj.birthday) >= SESSION_LIVE or obj.freq >= SESSION_ROUNDS 
        local too_old = false 
        if alive and not too_old then 
            obj:send(alive) 
        end
        if too_old and alive  then 
            obj:send("Connection: close\r\n") 
        end
        obj:send("Content-Type: plain/text\r\n")
        obj:send(format("Content-Length: %i\r\n", #data))
        obj:send("\r\n")
        obj:send(data)
    end,
    ['PUT'] = function(obj,verb, h,uri,proto)
        local data
        local alive = keep_alive(proto, h['connection'])
        if h['content-length'] then
           data=b:receive(h['content-length'])
        elseif h['transfer-encoding'] and h['transfer-encoding']:find('chunked') then
            local cnt
            local c={}
            repeat
                cnt,err,chunk = obj:receive()
                if cnt and #cnt > 0 then
                   c[#c+1]=obj:receive(cnt) 
                end
                obj:receive()
            until cnt == 0
            data = table.concat(c,"")
        else
            
        end
        obj:send(format("%s 200 OK\r\n", proto))
        local too_old = os.difftime(os.time(), obj.birthday) > SESSION_LIVE or obj.freq < SESSION_ROUNDS 
        if alive or not proto:find('1.0') then
            if too_old then obj:send("Connection: close\r\n")
            elseif alive then obj:send(alive) end
        end
        obj:send("Content-Type: plain/text\r\n")
        local r  = format("%s %s size %i\r\n", verb, uri, #data)
        obj:send(format("Content-Length: %i\r\n", #r))
        obj:send("\r\n")
        obj:send(r)
    end,
}

local connections = 0
local function handler(skt)
    skt:setoption('tcp-nodelay', true)
    local ip,port = skt:getsockname()
    local r_ip,port = skt:getpeername()
    local now = os.time()
    if not port then 
        skt:close()
        return 
    end
    
    local obj = copas.wrap(skt)

    obj:settimeout(120)
    connections = connections + 1
    
    --copas.sleep(10)

    local action,err,chunk = obj:receive()
    while action and #action > 0 do
        local h = {}
        local data
        local verb, resource, proto = action:match("^%s*([^%s]*)%s+([^%s]*)%s+(.*)$")
        if not verb then break end
        repeat
            local l,err,chunk = obj:receive()
            if l and #l > 0 then
                local k,v = header(l)
                h[k:lower()]=v:lower()
            end
        until not l or #l==0
        local keep_alive = (not proto:find("1.0") and h['connection'] ~= "close") or h['connection'] == 'keep-alive' 
        local f = dispatch[verb:upper()] or dispatch['H501'] or H501

        --start a thread for the sending, thus support pipeline mode if
        --HTTP 1.1 is specified
        if keep_alive and not proto:find("0.9") and not proto:find("1.0") then
            copas.addthread(function (x,...) 
                if obj.open then obj:open() end
                f(obj, verb, h, resource, proto) 
                if obj.close then obj:close() end
            end)
        else
            f(obj, verb, h, resource, proto) 
        end

        if keep_alive then 
            action, err, chunk = obj:receive()
        else action = nil end
    end
    connections = connections - 1
    if obj.close then obj:close() end
end


local function server(p)
    local tcp = socket.bind("*",tonumber(p))
    copas.addserver(tcp, handler)
end

server(8080)
copas.loop(0.1)
